package gu.sql2java;

import java.io.InputStream;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.nio.ByteBuffer;
import java.sql.Blob;
import java.sql.Clob;
import java.util.Date;

import com.google.common.base.Function;
import com.google.common.primitives.Primitives;

import gu.sql2java.exception.ResultSetCodecException;
import gu.sql2java.exception.UnsupportTypeException;
import static com.google.common.base.Preconditions.checkNotNull;
import static gu.sql2java.utils.ColumnTransformer.COLUMN_TRANSFORMER;

/**
 * 基本数据类型(Number,Boolean,Date,byte[],ByteBuffer)编解码实现
 * @author guyadong
 * @since 3.21.0
 *
 */
public class BaseTypeColumnCodec extends BaseColumnCodec {
	public static final BaseTypeColumnCodec BASE_CODEC = new BaseTypeColumnCodec();
	public BaseTypeColumnCodec() {
	}

	@SuppressWarnings("unchecked")
	@Override
	protected <T> T doDeserialize(Object columnValue, Class<T> targetType) throws ResultSetCodecException {
		return (T) transformColumn(columnValue, targetType);
	}

	@SuppressWarnings("unchecked")
	@Override
	protected <T> T doDeserialize(Object columnValue, Type targetType) throws ResultSetCodecException {
		if(!(targetType instanceof Class<?>)) {
			throw new UnsupportTypeException("UNSUPPORTED type of targetType " + targetType);
		}
		return (T) doDeserialize(columnValue,(Class<?>)targetType);
	}

	@SuppressWarnings("unchecked")
	@Override
	protected <T> T doSerialize(Object obj, Class<T> targetType) throws ResultSetCodecException {
		return (T) transformColumn(obj,targetType);
	}
	@SuppressWarnings("unchecked")
	protected Object transformColumn(Object input, Class<?> targetType) throws ResultSetCodecException {
		try {
			Function<Object, ?> fun = COLUMN_TRANSFORMER.getTransformer((Class<Object>)input.getClass(), targetType);
			if(null == fun) {
				throw new UnsupportTypeException("UNSUPPORTED transform  " + input.getClass().getName() + " to " + targetType.getName());	
			}
			return fun.apply(input);
		} catch (Exception e) {
			throw new ResultSetCodecException(e);
		}
	}

	/**
	 * 判断输入参数是否为SQL字段基本类型(number,boolean,string,date,byte array,byte buffer)
	 * @param object 为{@code null}返回{@code true}
	 */
	public static boolean isBaseColumnType(Object object){
		if(null == object){
			return true;
		}
		return isBaseColumnType(object.getClass());
	}
	/**
	 * 判断类型是否为SQL字段基本类型(number,boolean,string,date,byte array,byte buffer)
	 * @param clazz
	 */
	public static boolean isBaseColumnType(Class<?> clazz){
		Class<?> unwrapType = Primitives.unwrap(checkNotNull(clazz,"clazz is null"));
		if(unwrapType.isPrimitive() && !void.class.equals(unwrapType)){
			return true;
		}
		if(String.class.equals(clazz)){
			return true;
		}
		if(Date.class.isAssignableFrom(clazz)){
			return true;
		}
		if(ByteBuffer.class.isAssignableFrom(clazz)) {
			return true;
		}
		if(byte[].class.equals(clazz)){
			return true;
		}
		if(Enum.class.isAssignableFrom(clazz)){
			return true;
		}
		return false;
	}
	/**
	 * 判断类型是否为JDBC支持的基本类型(number,boolean,string,date,byte array,byte buffer)
	 * @param clazz
	 * @since 3.23.2
	 */
	public static boolean isJdbcType(Class<?> clazz){
		Class<?> unwrapType = Primitives.unwrap(checkNotNull(clazz,"clazz is null"));
		if(unwrapType.isPrimitive() && !void.class.equals(unwrapType)){
			return true;
		}
		if(String.class.equals(clazz)){
			return true;
		}
		if(BigDecimal.class.equals(clazz)){
			return true;
		}
		if(BigInteger.class.equals(clazz)){
			return true;
		}
		if(URL.class.equals(clazz)) {
			return true;
		}
		if(Date.class.isAssignableFrom(clazz)){
			return true;
		}
		if(InputStream.class.isAssignableFrom(clazz)){
			return true;
		}
		if(Blob.class.isAssignableFrom(clazz)){
			return true;
		}
		if(Clob.class.isAssignableFrom(clazz)){
			return true;
		}
		if(ByteBuffer.class.isAssignableFrom(clazz)) {
			return true;
		}
		if(byte[].class.equals(clazz)){
			return true;
		}
		return false;
	}
}
